package cn.shengchao.gankao;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.shengchao.base.enums.DisEnableStatusEnum;
import cn.shengchao.base.lock.DistributedLock;
import cn.shengchao.base.lock.ZLock;
import cn.shengchao.examstar.gankao.acl.ports.repositories.IGanKaoRepository;
import cn.shengchao.examstar.gankao.http.GanKaoHttpParams;
import cn.shengchao.examstar.gankao.http.GanKaoHttpUtil;
import cn.shengchao.examstar.gankao.http.GanKaoSpecialTags;
import cn.shengchao.examstar.gankao.infrastrure.po.*;
import cn.shengchao.examstar.product.acl.adapters.repositories.SpecificationTypeRepository;
import cn.shengchao.examstar.product.infrastructure.dao.ProductCourseDao;
import cn.shengchao.examstar.product.infrastructure.dao.ProductCourseNodesDao;
import cn.shengchao.examstar.product.infrastructure.dao.TProductCourseSettingDao;
import cn.shengchao.examstar.product.infrastructure.dao.TProductSpecificationTypeDao;
import cn.shengchao.examstar.product.infrastructure.dao.po.*;
import cn.shengchao.examstar.rpc.product.base.ContentProductTypeDefine;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.model.entity.BaseIdDO;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @program: examstarapi
 * @description: 赶考网数据综合处理类
 * @author: Xiong weiqiang
 * @create: 2025-06-23 17:57
 */
@Slf4j
@Service
public class GanKaoDomainService {
    @Autowired
    IGanKaoRepository ganKaoRepository;
    @Autowired
    GanKaoHttpUtil ganKaoHttpUtil;
    @Autowired
    DistributedLock distributedLock;
    @Autowired
    SpecificationTypeRepository specificationTypeRepository;
    @Autowired
    ProductCourseDao productCourseDao;
    @Autowired
    ProductCourseNodesDao productCourseNodesDao;
    @Autowired
    TProductSpecificationTypeDao tProductSpecificationTypeDao;
    @Autowired
    TProductCourseSettingDao tProductCourseSettingDao;

    @Value("${videoUrl}")
    private String videoUrl;

    @Transactional
    public void syncGanKaoCourseTagProductCourseSpecTypeRelation() {
        Long loadVersion = ganKaoRepository.getMaxFinishLoadVersion();
        List<GkCourse> gkCourses = ganKaoRepository.localCourseNeedCreateSpec(loadVersion, 100);
        if (CollUtil.isNotEmpty(gkCourses)) {
            Set<Integer> courseIds = gkCourses.stream().map(GkCourse::getCourseId).collect(Collectors.toSet());
            //实际课程和赶考课程id关系
            Map<Integer, Long> courseIdMap = courseIds.stream().collect(Collectors.toMap(v -> v, v -> Long.valueOf(loadVersion.toString().substring(0, 9) + "1" + String.format("%08d", v))));
            List<TProductCourseNodes> productCourseNodes = productCourseNodesDao.selectList(Wrappers.lambdaQuery(TProductCourseNodes.class).in(TProductCourseNodes::getCourseId, courseIdMap.values())
                    .eq(TProductCourseNodes::getIsLesson, 1)
            );
            Map<Long, List<TProductCourseNodes>> productCourseNodsMap = productCourseNodes.stream().collect(Collectors.groupingBy(TProductCourseNodes::getCourseId));
            courseIds.forEach(courseId -> {
                try {
                    Long localCourseId = courseIdMap.get(courseId);
                    List<TProductCourseNodes> productCourseNodeList = productCourseNodsMap.get(localCourseId);
                    boolean result = syncGanKaoCourseTagProductCourseSpecTypeRelationForSingleCourse(courseId, localCourseId, productCourseNodeList);
                    if (result) {
                        ganKaoRepository.updateCourseSpecStatus(loadVersion, courseId);
                    }
                    log.info("同步课程{}的标签关系{}", courseId, result ? "成功" : "失败");
                } catch (Exception e) {
                    log.error("同步课程规格关系失败", e);
                }
            });
        }

    }

    @Transactional
    private boolean syncGanKaoCourseTagProductCourseSpecTypeRelationForSingleCourse(Integer ganKaoCourseId, Long localCourseId, List<TProductCourseNodes> productCourseNodesList) {
        List<String> specTypeIdsForCourseType = ganKaoRepository.findLocalSpecTypeIdsFromSubjectMatchForGanKaoCourseId(ganKaoCourseId);
        List<String> specTypeIdsForCourseTag = ganKaoRepository.findLocalSpecTypeIdsFromTagMatchForGanKaoCourseId(ganKaoCourseId);
        Collection<String> specTypeIds = CollUtil.addAll(specTypeIdsForCourseType, specTypeIdsForCourseTag);
        boolean specTypeMatch = false;
        log.info("同步课程{}的类目匹配数量{}，课节数量{}", ganKaoCourseId, CollUtil.isEmpty(specTypeIds) ? 0 : specTypeIds.size(), CollUtil.isEmpty(productCourseNodesList) ? 0 : productCourseNodesList.size());
        if (CollUtil.isNotEmpty(specTypeIds) && CollUtil.isNotEmpty(productCourseNodesList)) {
            tProductSpecificationTypeDao.delete(Wrappers.lambdaQuery(TProductSpecificationType.class).eq(TProductSpecificationType::getProductId, localCourseId));
            specTypeIds.forEach(a -> {
                TProductSpecificationType tProductSpecificationType = new TProductSpecificationType();
                tProductSpecificationType.setProductId(localCourseId);
                tProductSpecificationType.setSpecificationType(Long.valueOf(a));
                tProductSpecificationTypeDao.insert(tProductSpecificationType);
            });
            List<Long> productIds = productCourseNodesList.stream().map(BaseIdDO::getId).toList();
            tProductCourseSettingDao.delete(Wrappers.lambdaQuery(TProductCourseSetting.class).in(TProductCourseSetting::getProductId, productIds));
            productCourseNodesList.forEach(a -> {
                TProductCourseSetting tProductCourseSetting = new TProductCourseSetting();
                tProductCourseSetting.setProductId(a.getId());
                tProductCourseSetting.setProductType(ContentProductTypeDefine.COURSE_GK.name());
                tProductCourseSetting.setCorpStartTime(DateUtil.parse("2025-07-01 00:00:00", "yyyy-MM-dd HH:mm:ss").toJdkDate());
                tProductCourseSetting.setCorpEndTime(DateUtil.parse("2026-07-01 00:00:00", "yyyy-MM-dd HH:mm:ss").toJdkDate());
                tProductCourseSetting.setUnitPayDay(180);
                tProductCourseSetting.setRefThirdId(NumberUtil.parseInt(a.getId().toString().substring(10)) + "");
                tProductCourseSetting.setDataStatus(1);
                tProductCourseSetting.setBatchId(localCourseId);
                tProductCourseSettingDao.insert(tProductCourseSetting);
            });
            specTypeMatch = true;
        }
        return specTypeMatch;
    }

    @Transactional
    public void syncCourseType2ToLocalCourse() {
        List<GkSubject> gkSubjectList = ganKaoRepository.listAllSubjects();
        List<Integer> gradeList = List.of(6, 9, 12);
        for (Integer gradeId : gradeList) {
            for (GkSubject gkSubject : gkSubjectList) {
                try {
                    requestCourseType2CatalogByGetShowCourseByGradeSubject(1750744297987L, gradeId, gkSubject, 2);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @Transactional
    public void syncGrade(Long parentId) {
        Long loadVersion = ganKaoRepository.getMaxFinishLoadVersion();
        if (loadVersion == 0L) {
            System.out.println("没有找到加载完成的版本");
            return;
        } else {
            System.out.println("找到加载完成的版本：" + loadVersion);
        }
        List<GkGradeSubject> gkGradeSubjectList = ganKaoRepository.listAllGradeSubjects();
        List<GkGrade> gkGradeList = ganKaoRepository.listAllGrades();
        List<GkSubject> gkSubjectList = ganKaoRepository.listAllSubjects();
        Map<Integer, GkSubject> subjectMap = gkSubjectList.stream().collect(Collectors.toMap(GkSubject::getSubjectId, Function.identity(), (old, current) -> current));
        for (GkGrade gkGrade : gkGradeList) {
            String path = parentId.toString();
            TSpecificationType specificationTypeGradeSave = new TSpecificationType();
            specificationTypeGradeSave.setName(gkGrade.getName() + "@GK");
            specificationTypeGradeSave.setSort(Long.valueOf(gkGrade.getGradeId()));
            specificationTypeGradeSave.setParentId(parentId);
            specificationTypeGradeSave.setStatus("1");
            specificationTypeGradeSave.setPath(path);
            specificationTypeGradeSave.setCreateUser(0L);
            specificationTypeGradeSave.setDescribe(gkGrade.getName());
            specificationTypeRepository.createSpecificationType(specificationTypeGradeSave);
            ganKaoRepository.saveGradeSpecMatch(gkGrade.getGradeId(), specificationTypeGradeSave.getId());
            List<Integer> subjectIds = gkGradeSubjectList.stream()
                    .filter(gkGradeSubject -> gkGradeSubject.getGradeId().equals(gkGrade.getGradeId()))
                    .map(GkGradeSubject::getSubjectId).distinct().toList();
            for (Integer subjectId : subjectIds) {
                String subjectPath = specificationTypeGradeSave.getPath() + "/" + specificationTypeGradeSave.getId();
                GkSubject gkSubject = subjectMap.get(subjectId);
                TSpecificationType specificationTypeSubjectSave = new TSpecificationType();
                specificationTypeSubjectSave.setName(gkSubject.getName() + "@GK");
                specificationTypeSubjectSave.setSort(gkSubject.getId());
                specificationTypeSubjectSave.setParentId(specificationTypeGradeSave.getId());
                specificationTypeSubjectSave.setPath(subjectPath);
                specificationTypeSubjectSave.setStatus("1");
                specificationTypeSubjectSave.setCreateUser(0L);
                specificationTypeSubjectSave.setDescribe(gkSubject.getAlias());
                specificationTypeRepository.createSpecificationType(specificationTypeSubjectSave);

                ganKaoRepository.saveSubjectSpecMatch(gkGrade.getGradeId(), gkSubject.getSubjectId(), specificationTypeSubjectSave.getId());
                List<GkSubjectCategory> gkSubjectCategories = ganKaoRepository.listSubjectCategorys(String.valueOf(loadVersion), gkGrade.getGradeId(), gkSubject.getSubjectId());

                if (CollUtil.isNotEmpty(gkSubjectCategories)) {
                    List<GkSubjectCategory> gkSubjectCateParentList = gkSubjectCategories.stream().filter(gkSubjectCategory -> gkSubjectCategory.getParentId() == 0).toList();
                    for (GkSubjectCategory gkSubjectCategoryParent : gkSubjectCateParentList) {
                        String subjectCategoryPath = specificationTypeSubjectSave.getPath() + "/" + specificationTypeSubjectSave.getId();
                        TSpecificationType specificationTypeCateParentSave = new TSpecificationType();
                        specificationTypeCateParentSave.setName(gkSubjectCategoryParent.getName() + "@GK");
                        specificationTypeCateParentSave.setSort(Long.valueOf(gkSubjectCategoryParent.getCateId()));
                        specificationTypeCateParentSave.setParentId(specificationTypeSubjectSave.getId());
                        specificationTypeCateParentSave.setPath(subjectCategoryPath);
                        specificationTypeCateParentSave.setStatus("1");
                        specificationTypeCateParentSave.setCreateUser(0L);
                        specificationTypeCateParentSave.setDescribe(gkSubjectCategoryParent.getName());
                        specificationTypeRepository.createSpecificationType(specificationTypeCateParentSave);
                        ganKaoRepository.saveSubjectCateSpecMatch(gkGrade.getGradeId(), gkSubject.getSubjectId(), gkSubjectCategoryParent.getCateId(), 0, specificationTypeCateParentSave.getId());
                        List<GkSubjectCategory> gkSubjectCateChildList = gkSubjectCategories.stream().filter(gkSubjectCategory -> gkSubjectCategory.getParentId().equals(gkSubjectCategoryParent.getCateId())).toList();
                        for (GkSubjectCategory gkSubjectCategory : gkSubjectCateChildList) {
                            ganKaoRepository.saveSubjectCateSpecMatch(gkGrade.getGradeId(), gkSubject.getSubjectId(), gkSubjectCategory.getCateId(), gkSubjectCategory.getParentId(), specificationTypeCateParentSave.getId());
                        }
                    }
                }
            }

        }
    }

    public void syncCourseTagToLocalCourse(String tag) {
        Long loadVersion = ganKaoRepository.getMaxFinishLoadVersion();
        String specTypeId = ganKaoRepository.findSpecTypeIdForCourseTag(tag);
        List<GkCourse> gkCourseList = ganKaoRepository.loadCourseTagNotSyncLocalCourse(loadVersion.toString(), tag, 200);
        if (CollUtil.isNotEmpty(gkCourseList)) {
            int size = 50;
            int loadSize = 0;
            List<CompletableFuture<Void>> futureList = new ArrayList<>();
            while (loadSize < gkCourseList.size()) {
                int loadIndex = loadSize + size;
                List<GkCourse> gkCourseSubList = gkCourseList.subList(loadSize, Math.min(loadIndex, gkCourseList.size()));
                loadSize = loadIndex;
                futureList.add(CompletableFuture.runAsync(() -> {
                    gkCourseSubList.forEach(gkCourse -> {
                        try {
                            syncSingleCourseForCourseTag(gkCourse, specTypeId);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
                }));
            }
            CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
            List<Integer> courseIds = gkCourseList.stream().map(GkCourse::getCourseId).toList();
            ganKaoRepository.updateCourseSyncStatusAndDetail(loadVersion, courseIds);
        }
    }

    public void syncCourseType1ToLocalCourse() {
        final int courseType = 1;
        Long loadVersion = ganKaoRepository.getMaxFinishLoadVersion();
        List<GkCourse> gkCourseList = ganKaoRepository.loadCourseTypeNotSyncLocalCourse(loadVersion.toString(), courseType, 200);
        if (CollUtil.isNotEmpty(gkCourseList)) {
            int size = 50;
            int loadSize = 0;
            List<CompletableFuture<Void>> futureList = new ArrayList<>();
            while (loadSize < gkCourseList.size()) {
                int loadIndex = loadSize + size;
                List<GkCourse> gkCourseSubList = gkCourseList.subList(loadSize, Math.min(loadIndex, gkCourseList.size()));
                loadSize = loadIndex;
                futureList.add(CompletableFuture.runAsync(() -> {
                    gkCourseSubList.forEach(gkCourse -> {
                        try {
                            syncSingleCourseForCourseType(gkCourse);
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
                }));
            }
            CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
            List<Integer> courseIds = gkCourseList.stream().map(GkCourse::getCourseId).toList();
            ganKaoRepository.updateCourseSyncStatusAndDetail(loadVersion, courseIds);
        }


    }

    public void syncCourseToLocalCourse() {
        Long loadVersion = ganKaoRepository.getMaxFinishLoadVersion();
        List<GkCourse> gkCourseList = ganKaoRepository.loadAllCourse(loadVersion);
        int size = 70;
        int loadSize = 0;
        List<CompletableFuture<Void>> futureList = new ArrayList<>();
        while (loadSize < gkCourseList.size()) {
            int loadIndex = loadSize + size;
            List<GkCourse> gkCourseSubList = gkCourseList.subList(loadSize, Math.min(loadIndex, gkCourseList.size()));
            loadSize = loadIndex;
            futureList.add(CompletableFuture.runAsync(() -> {
                gkCourseSubList.forEach(gkCourse -> {
                    try {
                        syncSingleCourseForCourseType(gkCourse);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
            }));
        }
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();
    }

    @Transactional
    protected void syncSingleCourseForCourseTag(GkCourse gkCourse, String specTypeId) {
        Map<Integer, Long> courseIdMap = new HashMap<>();
        Map<Integer, Long> lessionIdMap = new HashMap<>();
        List<GkLesson> lessons = ganKaoRepository.loadSessionByCourseId(gkCourse.getLoadVersion(), gkCourse.getCourseId());
        lessons = CollUtil.sort(lessons, Comparator.comparing(GkLesson::getName));
        List<GkSection> sections = ganKaoRepository.loadSectionByCourseIdAndLesson(gkCourse.getLoadVersion(), gkCourse.getCourseId(), null);
        TProductCourse tProductCourse = new TProductCourse();
        tProductCourse.setCardUrl(gkCourse.getTitlePic());
        tProductCourse.setId(Long.valueOf(gkCourse.getLoadVersion().toString().substring(0, 9) + "1" + String.format("%08d", gkCourse.getCourseId())));
        tProductCourse.setKnowledgeNodeNum(CollUtil.isNotEmpty(sections) ? sections.size() : 0);
        tProductCourse.setSpecificationType(specTypeId);
        tProductCourse.setName(gkCourse.getName());
        tProductCourse.setPrice(gkCourse.getPrice());
        tProductCourse.setDiscountPrice(gkCourse.getPrice());
        tProductCourse.setDetails(gkCourse.getDescription());
        tProductCourse.setDescription("");
        tProductCourse.setScore(0d);
        tProductCourse.setCourseType(1);
        tProductCourse.setImageUrl(gkCourse.getTitlePic());
        tProductCourse.setSort(gkCourse.getCourseId());
        tProductCourse.setStatus(DisEnableStatusEnum.DISABLE);
        tProductCourse.setVirtualPurchaseNum(0);
        tProductCourse.setActualPurchaseNum(0);
        tProductCourse.setIsVirtual(Boolean.TRUE);
        tProductCourse.setIsLogistics(Boolean.FALSE);
        tProductCourse.setDeleted(0L);
        tProductCourse.setClassHour(0.1d);
        productCourseDao.insertOrUpdate(tProductCourse);
        courseIdMap.put(gkCourse.getCourseId(), tProductCourse.getId());
        Map<Integer, GkLesson> lessonMap = lessons.stream().collect(Collectors.toMap(GkLesson::getLessonId, a -> a, (a, b) -> a));
        if (!lessonMap.isEmpty()) {
            List<GkLesson> sortedLessons = CollUtil.sort(new ArrayList<>(lessonMap.values()), Comparator.comparing(GkLesson::getName));
            for (GkLesson lesson : sortedLessons) {
                Integer lessonId = lesson.getLessonId();
                TProductCourseNodes tProductCourseNodes = new TProductCourseNodes();
                tProductCourseNodes.setId(Long.valueOf(gkCourse.getLoadVersion().toString().substring(0, 9) + "2" + String.format("%08d", lessonId)));
                tProductCourseNodes.setCourseId(tProductCourse.getId());
                tProductCourseNodes.setDuration(0d);
                tProductCourseNodes.setPlayUrl("");
                tProductCourseNodes.setTeacherId(null);
                tProductCourseNodes.setSpecificationType(specTypeId);
                tProductCourseNodes.setTeacherHeadUrl(gkCourse.getTeacherTitlePic());
                tProductCourseNodes.setTeacherName(gkCourse.getTeacherName());
                tProductCourseNodes.setName(lesson.getName());
                tProductCourseNodes.setSort(lesson.getLessonId());
                tProductCourseNodes.setStatus(DisEnableStatusEnum.ENABLE);
                tProductCourseNodes.setVirtualPurchaseNum(0);
                tProductCourseNodes.setActualPurchaseNum(0);
                tProductCourseNodes.setIsVirtual(Boolean.TRUE);
                tProductCourseNodes.setIsLogistics(Boolean.FALSE);
                tProductCourseNodes.setPrice(BigDecimal.ZERO);
                tProductCourseNodes.setParentId(tProductCourse.getId());
                tProductCourseNodes.setDescription(lesson.getName());
                tProductCourseNodes.setDetails("");
                tProductCourseNodes.setImageUrl(null);
                tProductCourseNodes.setScore(null);
                tProductCourseNodes.setFileList("[]");
                tProductCourseNodes.setDeleted(0L);
                tProductCourseNodes.setIsLesson(0L);
                productCourseNodesDao.insertOrUpdate(tProductCourseNodes);
                lessionIdMap.put(lesson.getLessonId(), tProductCourseNodes.getId());
            }
        }
        Map<Integer, GkSection> sectionMap = sections.stream().collect(Collectors.toMap(GkSection::getSectionId, a -> a, (a, b) -> a));
        if (!sectionMap.isEmpty()) {
            List<GkSection> sortedSections = CollUtil.sort(new ArrayList<>(sectionMap.values()), Comparator.comparing(GkSection::getName));
            for (GkSection section : sortedSections) {
                Integer sectionId = section.getSectionId();
                TProductCourseNodes tProductCourseNodes = new TProductCourseNodes();
                tProductCourseNodes.setId(Long.valueOf(gkCourse.getLoadVersion().toString().substring(0, 9) + "3" + String.format("%08d", sectionId)));
                tProductCourseNodes.setCourseId(courseIdMap.get(section.getCourseId()));
                tProductCourseNodes.setDuration(0d);
                tProductCourseNodes.setPlayUrl(videoUrl+"gankao/" + sectionId + "@" + section.getCourseId() + ".mp4");
                tProductCourseNodes.setSpecificationType(specTypeId);
                tProductCourseNodes.setTeacherId(null);
                tProductCourseNodes.setTeacherHeadUrl(gkCourse.getTeacherTitlePic());
                tProductCourseNodes.setTeacherName(gkCourse.getTeacherName());
                tProductCourseNodes.setName(section.getName());
                tProductCourseNodes.setSort(section.getSectionId());
                tProductCourseNodes.setStatus(DisEnableStatusEnum.ENABLE);
                tProductCourseNodes.setVirtualPurchaseNum(0);
                tProductCourseNodes.setActualPurchaseNum(0);
                tProductCourseNodes.setIsVirtual(Boolean.TRUE);
                tProductCourseNodes.setIsLogistics(Boolean.FALSE);
                if (ObjectUtil.defaultIfNull(section.getFree(), 0) == 1) {
                    tProductCourseNodes.setPrice(BigDecimal.ZERO);
                    tProductCourseNodes.setDiscountPrice(BigDecimal.ZERO);
                    tProductCourseNodes.setForceFree(1);
                } else {
                    tProductCourseNodes.setPrice(BigDecimal.valueOf(5.0d));
                    tProductCourseNodes.setDiscountPrice(BigDecimal.valueOf(5.0d));
                }
                tProductCourseNodes.setParentId(lessionIdMap.get(section.getLessonId()));
                tProductCourseNodes.setDescription(section.getName());
                tProductCourseNodes.setDetails("");
                tProductCourseNodes.setImageUrl(section.getCoverImg());
                tProductCourseNodes.setVideoId(section.getVideoId() != null ? Long.valueOf(section.getVideoId()) : null);
                tProductCourseNodes.setScore(null);
                tProductCourseNodes.setFileList("[]");
                tProductCourseNodes.setDeleted(0L);
                tProductCourseNodes.setIsLesson(1L);
                productCourseNodesDao.insertOrUpdate(tProductCourseNodes);
            }
        }
    }

    @Transactional
    protected void syncSingleCourseForCourseType(GkCourse gkCourse) {
        Map<Integer, Long> courseIdMap = new HashMap<>();
        Map<Integer, Long> lessionIdMap = new HashMap<>();
        List<GkLesson> lessons = ganKaoRepository.loadSessionByCourseId(gkCourse.getLoadVersion(), gkCourse.getCourseId());
        lessons = CollUtil.sort(lessons, Comparator.comparing(GkLesson::getName));
        List<GkSection> sections = ganKaoRepository.loadSectionByCourseIdAndLesson(gkCourse.getLoadVersion(), gkCourse.getCourseId(), null);
        List<GkSpecTypeMatch> specificationTypes = ganKaoRepository.listSpecificationTypeIdByCateId(gkCourse.getCateId());
        String specTypeId = "0";
        if (CollUtil.isNotEmpty(specificationTypes)) {
            specTypeId = specificationTypes.getFirst().getSpecificationTypeId();
        }
        TProductCourse tProductCourse = new TProductCourse();
        tProductCourse.setCardUrl(gkCourse.getTitlePic());
        tProductCourse.setId(Long.valueOf(gkCourse.getLoadVersion().toString().substring(0, 9) + "1" + String.format("%08d", gkCourse.getCourseId())));
        tProductCourse.setKnowledgeNodeNum(CollUtil.isNotEmpty(sections) ? sections.size() : 0);
        tProductCourse.setSpecificationType(specTypeId);
        tProductCourse.setName(gkCourse.getName());
        tProductCourse.setPrice(gkCourse.getPrice());
        tProductCourse.setDiscountPrice(gkCourse.getPrice());
        tProductCourse.setDetails(gkCourse.getDescription());
        tProductCourse.setDescription("");
        tProductCourse.setCourseType(1);
        tProductCourse.setScore(0d);
        tProductCourse.setImageUrl(gkCourse.getTitlePic());
        tProductCourse.setSort(gkCourse.getCourseId());
        tProductCourse.setStatus(DisEnableStatusEnum.DISABLE);
        tProductCourse.setVirtualPurchaseNum(0);
        tProductCourse.setActualPurchaseNum(0);
        tProductCourse.setIsVirtual(Boolean.TRUE);
        tProductCourse.setIsLogistics(Boolean.FALSE);
        tProductCourse.setDeleted(0L);
        tProductCourse.setClassHour(0.1d);
        productCourseDao.insertOrUpdate(tProductCourse);
        courseIdMap.put(gkCourse.getCourseId(), tProductCourse.getId());
        Map<Integer, GkLesson> lessonMap = lessons.stream().collect(Collectors.toMap(GkLesson::getLessonId, a -> a, (a, b) -> a));
        if (!lessonMap.isEmpty()) {
            List<GkLesson> sortedLessons = CollUtil.sort(new ArrayList<>(lessonMap.values()), Comparator.comparing(GkLesson::getName));
            for (GkLesson lesson : sortedLessons) {
                Integer lessonId = lesson.getLessonId();
                TProductCourseNodes tProductCourseNodes = new TProductCourseNodes();
                tProductCourseNodes.setId(Long.valueOf(gkCourse.getLoadVersion().toString().substring(0, 9) + "2" + String.format("%08d", lessonId)));
                tProductCourseNodes.setCourseId(tProductCourse.getId());
                tProductCourseNodes.setDuration(0d);
                tProductCourseNodes.setPlayUrl("");
                tProductCourseNodes.setTeacherId(null);
                tProductCourseNodes.setSpecificationType(specTypeId);
                tProductCourseNodes.setTeacherHeadUrl(gkCourse.getTeacherTitlePic());
                tProductCourseNodes.setTeacherName(gkCourse.getTeacherName());
                tProductCourseNodes.setName(lesson.getName());
                tProductCourseNodes.setSort(lesson.getSort());
                tProductCourseNodes.setStatus(DisEnableStatusEnum.ENABLE);
                tProductCourseNodes.setVirtualPurchaseNum(0);
                tProductCourseNodes.setActualPurchaseNum(0);
                tProductCourseNodes.setIsVirtual(Boolean.TRUE);
                tProductCourseNodes.setIsLogistics(Boolean.FALSE);
                tProductCourseNodes.setPrice(BigDecimal.ZERO);
                tProductCourseNodes.setParentId(tProductCourse.getId());
                tProductCourseNodes.setDescription(lesson.getName());
                tProductCourseNodes.setDetails("");
                tProductCourseNodes.setImageUrl(null);
                tProductCourseNodes.setScore(null);
                tProductCourseNodes.setFileList("[]");
                tProductCourseNodes.setDeleted(0L);
                tProductCourseNodes.setIsLesson(0L);
                productCourseNodesDao.insertOrUpdate(tProductCourseNodes);
                lessionIdMap.put(lesson.getLessonId(), tProductCourseNodes.getId());
            }
        }
        Map<Integer, GkSection> sectionMap = sections.stream().collect(Collectors.toMap(GkSection::getSectionId, a -> a, (a, b) -> a));
        if (!sectionMap.isEmpty()) {
            List<GkSection> sortedSections = CollUtil.sort(new ArrayList<>(sectionMap.values()), Comparator.comparing(GkSection::getName));
            for (GkSection section : sortedSections) {
                Integer sectionId = section.getSectionId();
                TProductCourseNodes tProductCourseNodes = new TProductCourseNodes();
                tProductCourseNodes.setId(Long.valueOf(gkCourse.getLoadVersion().toString().substring(0, 9) + "3" + String.format("%08d", sectionId)));
                tProductCourseNodes.setCourseId(courseIdMap.get(section.getCourseId()));
                tProductCourseNodes.setDuration(0d);
                tProductCourseNodes.setPlayUrl(videoUrl+"gankao/" + sectionId + "@" + section.getCourseId() + ".mp4");
                tProductCourseNodes.setSpecificationType(specTypeId);
                tProductCourseNodes.setTeacherId(null);
                tProductCourseNodes.setTeacherHeadUrl(gkCourse.getTeacherTitlePic());
                tProductCourseNodes.setTeacherName(gkCourse.getTeacherName());
                tProductCourseNodes.setName(section.getName());
                tProductCourseNodes.setSort(section.getSort());
                tProductCourseNodes.setStatus(DisEnableStatusEnum.ENABLE);
                tProductCourseNodes.setDuration(Double.valueOf(section.getDuration()));
                tProductCourseNodes.setVirtualPurchaseNum(0);
                tProductCourseNodes.setActualPurchaseNum(0);
                tProductCourseNodes.setIsVirtual(Boolean.TRUE);
                tProductCourseNodes.setIsLogistics(Boolean.FALSE);
                if (ObjectUtil.defaultIfNull(section.getFree(), 0) == 1) {
                    tProductCourseNodes.setPrice(BigDecimal.ZERO);
                    tProductCourseNodes.setDiscountPrice(BigDecimal.ZERO);
                } else {
                    if (ObjectUtil.defaultIfNull(gkCourse.getGradeId(), 0) > 9) {
                        tProductCourseNodes.setPrice(BigDecimal.valueOf(9d));
                        tProductCourseNodes.setDiscountPrice(BigDecimal.valueOf(6.2d));
                    } else if (ObjectUtil.defaultIfNull(gkCourse.getGradeId(), 0) < 10 && ObjectUtil.defaultIfNull(gkCourse.getGradeId(), 0) > 6) {
                        tProductCourseNodes.setPrice(BigDecimal.valueOf(3.2d));
                        tProductCourseNodes.setDiscountPrice(BigDecimal.valueOf(1.8d));
                    } else {
                        tProductCourseNodes.setPrice(BigDecimal.valueOf(3.2d));
                        tProductCourseNodes.setDiscountPrice(BigDecimal.valueOf(1.8d));
                    }
                }
                tProductCourseNodes.setParentId(lessionIdMap.get(section.getLessonId()));
                tProductCourseNodes.setDescription(section.getName());
                tProductCourseNodes.setDetails("");
                tProductCourseNodes.setImageUrl(section.getCoverImg());
                tProductCourseNodes.setVideoId(section.getVideoId() != null ? Long.valueOf(section.getVideoId()) : null);
                tProductCourseNodes.setScore(null);
                tProductCourseNodes.setFileList("[]");
                tProductCourseNodes.setDeleted(0L);
                tProductCourseNodes.setIsLesson(1L);
                productCourseNodesDao.insertOrUpdate(tProductCourseNodes);
            }
        }
    }

    @Transactional
    public void requestDataByLoadVersion(Long loadVersion) throws Exception {
        // 使用分布式锁确保同一订单退款操作的幂等性
        String lockKey = "GANKAO_LOAD_LOCK:" + loadVersion;
        ZLock zLock = null;
        try {
            // 尝试获取锁，等待最多3秒，锁定30秒
            zLock = distributedLock.tryLock(lockKey, 3, TimeUnit.SECONDS);
            CheckUtils.throwIf(zLock == null, "获取锁失败，请稍后重试");
            List<GkGrade> gkGradeList = new ArrayList<>();
            List<GkSubject> gkSubjectList = new ArrayList<>();
            List<GkGradeSubject> gradeSubjectList = new ArrayList<>();
            requestDataByGetSearchCriteria(loadVersion, gkGradeList, gkSubjectList, gradeSubjectList);

            ganKaoRepository.saveBatchGrade(gkGradeList);
            ganKaoRepository.saveBatchSubject(gkSubjectList);
            ganKaoRepository.saveBatchGradeSubject(gradeSubjectList);
            List<Integer> courseTypes = List.of(1, 2, 3, 5, 6);
            if (CollUtil.isNotEmpty(gradeSubjectList)) {
                for (GkGradeSubject gradeSubject : gradeSubjectList) {
                    for (Integer courseType : courseTypes) {
                        requestDataByGetShowCourseByGradeSubject(loadVersion, gradeSubject.getGradeId(), gradeSubject.getSubjectId(), courseType);
                    }
                }
            }
            ganKaoRepository.finishDataLoadTaskByLoadVersion(loadVersion);
        } finally {
            if (zLock != null) {
                zLock.close();
            }
        }

    }

    public void requestCourseType2CatalogByGetShowCourseByGradeSubject(Long loadVersion, int gradeId, GkSubject gkSubject, int courseType) throws Exception {
        int pageSize = 1000;
        int currentPage = 0;
        Map<String, Object> showCoursesParams = new HashMap<>();
        showCoursesParams.put("grade_id", gradeId);
        showCoursesParams.put("subject_id", gkSubject.getSubjectId());
        showCoursesParams.put("page", currentPage);
        showCoursesParams.put("course_type", courseType);
        showCoursesParams.put("take_number", pageSize);
        String result1 = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_SHOW_COURSES_API, showCoursesParams);
        JSONObject result = new JSONObject(result1);
        String courseName = "";
        if (result != null) {
            if (gradeId < 7) {
                courseName = "小学" + gkSubject.getName() + "课程知识点汇总@GK";
            } else if (gradeId < 10) {
                courseName = "初中" + gkSubject.getName() + "课程知识点汇总@GK";
            } else {
                courseName = "高中" + gkSubject.getName() + "课程知识点汇总@GK";
            }
            Integer genId = Integer.parseInt(gradeId + "" + gkSubject.getSubjectId());
            String courseId = "175074429" + "41" + String.format("%07d", genId);

            TProductCourse tProductCourse = new TProductCourse();
            tProductCourse.setCardUrl("");
            tProductCourse.setId(Long.valueOf(courseId));
            tProductCourse.setKnowledgeNodeNum(0);
            tProductCourse.setSpecificationType(0 + "");
            tProductCourse.setName(courseName);
            tProductCourse.setPrice(BigDecimal.valueOf(9999d));
            tProductCourse.setDiscountPrice(BigDecimal.valueOf(9999d));
            tProductCourse.setDetails("");
            tProductCourse.setDescription("");
            tProductCourse.setCourseType(1);
            tProductCourse.setScore(0d);
            tProductCourse.setImageUrl("");
            tProductCourse.setSort(gkSubject.getSubjectId());
            tProductCourse.setStatus(DisEnableStatusEnum.DISABLE);
            tProductCourse.setVirtualPurchaseNum(0);
            tProductCourse.setActualPurchaseNum(0);
            tProductCourse.setIsVirtual(Boolean.TRUE);
            tProductCourse.setIsLogistics(Boolean.FALSE);
            tProductCourse.setDeleted(0L);
            tProductCourse.setClassHour(0.1d);
            tProductCourse.setStatus(DisEnableStatusEnum.DISABLE);
            List<TProductCourseNodes> productCourseNodesList = new ArrayList<>();

            if (result.getInt("code") == 10000) {
                JSONArray guides = result.getJSONObject("data").getJSONArray("guide");
                analyzeCourseType2Catalog(guides, tProductCourse, productCourseNodesList, courseId);
            }
            System.out.println("tProductCourse:" + JSONUtil.toJsonStr(tProductCourse));
            System.out.println("productCourseNodesList:" + JSONUtil.toJsonStr(productCourseNodesList));
        }

    }

    public void analyzeCourseType2Catalog(JSONArray childs, TProductCourse tProductCourse, List<TProductCourseNodes> productCourseNodesList, String parentId) throws Exception {
        if (CollUtil.isNotEmpty(childs)) {
            int i = 0;
            for (Object child : childs) {
                i++;
                JSONObject childJson = new JSONObject(child);
                Integer cateId = Integer.parseInt(childJson.getStr("id"));
                String lessonId = "17507442942" + String.format("%07d", cateId);
                TProductCourseNodes tProductCourseNodes = new TProductCourseNodes();
                tProductCourseNodes.setId(Long.valueOf(lessonId));
                tProductCourseNodes.setCourseId(tProductCourse.getId());
                tProductCourseNodes.setDuration(0d);
                tProductCourseNodes.setPlayUrl("");
                tProductCourseNodes.setTeacherId(null);
                tProductCourseNodes.setSpecificationType(lessonId.toString());
                tProductCourseNodes.setTeacherHeadUrl("");
                tProductCourseNodes.setTeacherName("");
                tProductCourseNodes.setName(childJson.getStr("name"));
                tProductCourseNodes.setSort(i);
                tProductCourseNodes.setStatus(DisEnableStatusEnum.ENABLE);
                tProductCourseNodes.setVirtualPurchaseNum(0);
                tProductCourseNodes.setActualPurchaseNum(0);
                tProductCourseNodes.setIsVirtual(Boolean.TRUE);
                tProductCourseNodes.setIsLogistics(Boolean.FALSE);
                tProductCourseNodes.setPrice(BigDecimal.ZERO);
                tProductCourseNodes.setParentId(Long.valueOf(parentId));
                tProductCourseNodes.setDescription(childJson.getStr("name"));
                tProductCourseNodes.setDetails("");
                tProductCourseNodes.setImageUrl(null);
                tProductCourseNodes.setScore(null);
                tProductCourseNodes.setFileList("[]");
                tProductCourseNodes.setDeleted(0L);
                tProductCourseNodes.setIsLesson(0L);
                productCourseNodesList.add(tProductCourseNodes);
                if (childJson.containsKey("child") && CollUtil.isNotEmpty(childJson.getJSONArray("child"))) {
                    analyzeCourseType2Catalog(childJson.getJSONArray("child"), tProductCourse, productCourseNodesList, lessonId);
                }
            }
        } else {
            return;
        }
    }

    public void requestDataByGetShowCourseByGradeSubject(Long loadVersion, int gradeId, int subjectId, int courseType) throws Exception {
        int pageSize = 1000;
        int currentPage = 0;
        int lastPage = 1;
        while (currentPage < lastPage) {
            currentPage++;
            JSONObject result = requestDataByGetShowCourseByGradeSubjectAndPage(loadVersion, gradeId, subjectId, courseType, currentPage, pageSize);
            lastPage = result.getInt("last_page");
        }
    }

    public void requestDataByGetSearchCriteria(Long loadVersion, List<GkGrade> gkGradeList,
                                               List<GkSubject> gkSubjectList,
                                               List<GkGradeSubject> gradeSubjectList) throws Exception {
        String result = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_GET_SEARCH_CRITERIA_API, new HashMap<>());
        if (result != null && JSONUtil.isTypeJSON(result)) {
            JSONObject jsonObject = new JSONObject(result);
            if (jsonObject.getInt("code") == 10000) {
                JSONArray grades = jsonObject.getJSONObject("data").getJSONArray("grades");
                if (grades != null) {

                    grades.forEach(grade -> {
                        JSONObject gradeObject = (JSONObject) grade;
                        GkGrade gkGrade = new GkGrade();
                        Integer gradeId = gradeObject.getInt("id");
                        gkGrade.setGradeId(gradeId);
                        gkGrade.setName(gradeObject.getStr("name"));
                        gkGrade.setLoadVersion(loadVersion);
                        gkGradeList.add(gkGrade);
                        JSONArray subjects = new JSONObject(grade).getJSONArray("subjects");
                        if (subjects != null) {
                            subjects.forEach(s -> {
                                JSONObject subjectObject = (JSONObject) s;
                                GkSubject gkSubject = new GkSubject();
                                Integer subjectId = subjectObject.getInt("id");
                                GkGradeSubject gradeSubject = new GkGradeSubject();
                                gradeSubject.setGradeId(gradeId);
                                gradeSubject.setSubjectId(subjectId);
                                gradeSubject.setLoadVersion(loadVersion);
                                gradeSubjectList.add(gradeSubject);
                                gkSubject.setSubjectId(subjectId);
                                gkSubject.setName(subjectObject.getStr("name"));
                                gkSubject.setAlias(subjectObject.getStr("alias"));
                                gkSubject.setNoCourse(subjectObject.getStr("noCourse"));
                                gkSubject.setIsSynchro(subjectObject.getStr("is_synchro"));
                                gkSubject.setWorkSubjectId(subjectObject.getInt("work_subject_id"));
                                gkSubject.setSroomCanupload(subjectObject.getStr("sroom_canupload"));
                                gkSubject.setLoadVersion(loadVersion);
                                //subject科目是共用的
                                if (!gkSubjectList.stream().map(GkSubject::getSubjectId).toList().contains(subjectId)) {
                                    gkSubjectList.add(gkSubject);
                                }
                            });
                        }
                    });
                }
            }
        }
    }

    public JSONObject requestDataByGetShowCourseByGradeSubjectAndPage(Long loadVersion, int gradeId, int subjectId, int courseType, int currentPage, int pageSize) throws Exception {
        log.debug("正在加载课程：年级id：{}，科目id:{},课程类型:{},当前页，{}页", gradeId, subjectId, courseType, currentPage);
        List<GkSubjectCategory> gkSubjectCategories = new ArrayList<>();
        List<GkSubjectCategoryCourseTypeTag> gkSubjectCategoryCourseTypeTagList = new ArrayList<>();
        List<GkCourse> gkCourses = new ArrayList<>();
        JSONObject mapResult = new JSONObject();
        mapResult.set("page", currentPage);
        int lastPage = currentPage;
        Map<String, Object> showCoursesParams = new HashMap<>();
        showCoursesParams.put("grade_id", gradeId);
        showCoursesParams.put("subject_id", subjectId);
        showCoursesParams.put("page", currentPage);
        showCoursesParams.put("course_type", courseType);
        showCoursesParams.put("take_number", pageSize);
        String result1 = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_SHOW_COURSES_API, showCoursesParams);
        if (result1 != null && JSONUtil.isTypeJSON(result1)) {
            JSONObject jsonObject = new JSONObject(result1);
            if (jsonObject.getInt("code") == 10000) {
                JSONArray guides = jsonObject.getJSONObject("data").getJSONArray("guide");
                if (guides != null) {
                    guides.forEach(guide -> {
                        JSONObject guideObject = (JSONObject) guide;
                        Integer cateId = guideObject.getInt("id");
                        GkSubjectCategory gkSubjectCategory = new GkSubjectCategory();
                        gkSubjectCategory.setCateId(cateId);
                        gkSubjectCategory.setSubjectId(subjectId);
                        gkSubjectCategory.setGradeId(gradeId);
                        gkSubjectCategory.setLoadVersion(loadVersion);
                        gkSubjectCategory.setCourseType(courseType);
                        gkSubjectCategory.setParentId(guideObject.getInt("parent_id", 0));
                        gkSubjectCategory.setName(guideObject.getStr("name"));
                        gkSubjectCategory.setOntree(guideObject.getStr("ontree"));
                        gkSubjectCategory.setVersion(guideObject.getStr("version"));
                        gkSubjectCategory.setType(guideObject.getStr("type"));
                        gkSubjectCategory.setOntree(guideObject.getStr("ontree"));
                        gkSubjectCategories.add(gkSubjectCategory);
                        JSONArray childArray = guideObject.getJSONArray("child");
                        if (childArray != null) {
                            for (Object child : childArray) {
                                JSONObject childObject = (JSONObject) child;
                                GkSubjectCategory childGkSubjectCategory = new GkSubjectCategory();
                                Integer childCateId = childObject.getInt("id");
                                childGkSubjectCategory.setCateId(childCateId);
                                childGkSubjectCategory.setSubjectId(subjectId);
                                childGkSubjectCategory.setGradeId(gradeId);
                                childGkSubjectCategory.setLoadVersion(loadVersion);
                                childGkSubjectCategory.setCourseType(courseType);
                                childGkSubjectCategory.setParentId(childObject.getInt("parent_id", 0));
                                childGkSubjectCategory.setName(childObject.getStr("name"));
                                childGkSubjectCategory.setVersion(childObject.getStr("version"));
                                childGkSubjectCategory.setType(childObject.getStr("type"));
                                childGkSubjectCategory.setOntree(childObject.getStr("ontree"));
                                gkSubjectCategories.add(childGkSubjectCategory);
                            }
                        }
                    });
                }
                JSONArray courses = jsonObject.getJSONObject("data").getJSONArray("courses");
                if (courses != null) {
                    courses.forEach(course -> {
                        JSONObject courseObject = (JSONObject) course;
                        GkCourse gkCourse = new GkCourse();
                        Integer courseId = courseObject.getInt("id");
                        gkCourse.setCourseId(courseId);
                        gkCourse.setName(courseObject.getStr("name"));
                        gkCourse.setCateId(courseObject.getInt("cateId"));
                        gkCourse.setSubjectId(courseObject.getInt("subject_id"));
                        gkCourse.setPrice(courseObject.getBigDecimal("price"));
                        gkCourse.setParentCateId(courseObject.getInt("parent_cateId"));
                        gkCourse.setGradeId(courseObject.getInt("grade_id"));
                        gkCourse.setMediaType(courseObject.getInt("media_type"));
                        gkCourse.setTitlePic(courseObject.getStr("title_pic"));
                        gkCourse.setViews(courseObject.getStr("views"));
                        gkCourse.setDing(courseObject.getStr("ding"));
                        gkCourse.setIsLoose(courseObject.getStr("is_loose"));
                        gkCourse.setTeacherId(courseObject.getInt("teacherId"));
                        gkCourse.setTeacherName(courseObject.getStr("teacherName"));
                        gkCourse.setTeacherTitlePic(courseObject.getStr("teacherTitlePic"));
                        gkCourse.setCreatedAt(courseObject.getStr("created_at"));
                        gkCourse.setRevisionNum(courseObject.getInt("revision_num"));
                        gkCourse.setVersionPicOrg(courseObject.getStr("version_pic_org"));
                        gkCourse.setPic(courseObject.getStr("pic"));
                        gkCourse.setLoadStatus(0);
                        gkCourse.setLoadVersion(loadVersion);
                        GkSubjectCategoryCourseTypeTag gkSubjectCategoryCourseTypeTag = new GkSubjectCategoryCourseTypeTag();
                        gkSubjectCategoryCourseTypeTag.setLoadVersion(loadVersion);
                        gkSubjectCategoryCourseTypeTag.setCourseId(courseId);
                        gkSubjectCategoryCourseTypeTag.setSubjectId(subjectId);
                        gkSubjectCategoryCourseTypeTag.setCateId(gkCourse.getCateId());
                        gkSubjectCategoryCourseTypeTag.setParentId(gkCourse.getParentCateId());
                        gkSubjectCategoryCourseTypeTag.setGradeId(gradeId);
                        gkSubjectCategoryCourseTypeTag.setTag(Integer.valueOf(subjectId).toString());
                        gkSubjectCategoryCourseTypeTag.setCourseType(ObjectUtil.defaultIfNull(courseType, 0));
                        gkCourses.add(gkCourse);
                        gkSubjectCategoryCourseTypeTagList.add(gkSubjectCategoryCourseTypeTag);
                    });
                }
                lastPage = jsonObject.getJSONObject("data").getJSONObject("page").getInt("last_page");
                ganKaoRepository.saveBatchCourseCategoryByCheck(loadVersion, gradeId, subjectId, gkSubjectCategories);
                ganKaoRepository.saveBatchCourse(gkCourses);
                ganKaoRepository.saveBatchCourseCategoryCourseTypeTag(gkSubjectCategoryCourseTypeTagList);
                log.debug("已加载课程：年级id：{}，科目id:{},课程类型:{},当前页{}页,共{}页", gradeId, subjectId, courseType, currentPage, lastPage);
            }
        }
        mapResult.set("last_page", lastPage);
        return mapResult;
    }

    public void requestDataByGetCourseDetail(Long loadVersion) throws Exception {
        if (loadVersion == 0L) {
            loadVersion = null;
        }
        List<GkCourse> gkCourses = ganKaoRepository.loadNotLoadDetailCourse(loadVersion);
        if (CollUtil.isNotEmpty(gkCourses)) {
            List<Long> courseDataIds = new ArrayList<>();
            List<GkLesson> lessons = new ArrayList<>();
            List<GkSection> sections = new ArrayList<>();
            Map<Integer, String> courseDetailMap = new HashMap<>();
            gkCourses.forEach(gkCourse -> {
                courseDataIds.add(gkCourse.getId());
                log.debug("正在加载课程详情：{},年级：{},", gkCourse.getName(), gkCourse.getGradeId());
                Map<String, Object> courseDetailParams = new HashMap<>();
                courseDetailParams.put("course_id", gkCourse.getCourseId());
                String result = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_GET_COURSE_DETAIL_API, courseDetailParams);
                if (result != null && JSONUtil.isTypeJSON(result)) {
                    JSONObject jsonObject = new JSONObject(result);
                    if (jsonObject.getInt("code") == 10000) {
                        String description = jsonObject.getJSONObject("data").getStr("description");
                        courseDetailMap.put(gkCourse.getCourseId(), description);
                        JSONArray lessonsArray = jsonObject.getJSONObject("data").getJSONArray("lessons");
                        if (lessonsArray != null) {
                            for (int lessonIndex = 0; lessonIndex < lessonsArray.size(); lessonIndex++) {
                                JSONObject lessonObject = lessonsArray.getJSONObject(lessonIndex);
                                GkLesson lesson = new GkLesson();
                                Integer lessonId = lessonObject.getInt("id");
                                lesson.setLessonId(lessonId);
                                lesson.setCourseId(gkCourse.getCourseId());
                                lesson.setName(lessonObject.getStr("name"));
                                lesson.setLoadVersion(gkCourse.getLoadVersion());
                                lesson.setSort(lessonIndex);
                                lessons.add(lesson);
                                JSONArray sectionArray = lessonObject.getJSONArray("section");
                                if (sectionArray != null) {
                                    for (int sectionIndex = 0; sectionIndex < sectionArray.size(); sectionIndex++) {
                                        JSONObject sectionObject = (JSONObject) sectionArray.get(sectionIndex);
                                        GkSection section = new GkSection();
                                        Integer sectionId = sectionObject.getInt("id");
                                        section.setSectionId(sectionId);
                                        section.setLessonId(lessonId);
                                        section.setSort(sectionIndex);
                                        section.setName(sectionObject.getStr("name"));
                                        section.setCcvid(sectionObject.getStr("ccvid"));
                                        section.setHasQuestion(sectionObject.getInt("has_question"));
                                        section.setInProduction(sectionObject.getBool("inProduction"));
                                        section.setVideoId(sectionObject.getStr("video_id"));
                                        section.setCoverImg(sectionObject.getStr("cover_img"));
                                        section.setUnitTestAddress(sectionObject.getStr("unitTestAddress"));
                                        section.setDuration(sectionObject.getInt("duration"));
                                        section.setFree(sectionObject.getInt("free"));
                                        section.setType(sectionObject.getInt("type"));
                                        section.setCourseId(gkCourse.getCourseId());
                                        section.setLoadVersion(gkCourse.getLoadVersion());
                                        sections.add(section);
                                    }
                                }
                            }
                        }
                    }
                }
            });
            ganKaoRepository.saveBatchLesson(courseDataIds, lessons, sections);
            ganKaoRepository.updateCourseLoadStatusAndDetail(loadVersion, courseDetailMap);
            ganKaoRepository.finishCourseDetailLoadTaskByLoadVersion(loadVersion);
        }

    }

    @Transactional
    public void requestDataByGetCourseDetailForSpecialTag(Long loadVersion, String tag) {
        if (loadVersion == 0L) {
            loadVersion = null;
        }
        List<GkCourse> gkCourses = ganKaoRepository.loadNotLoadDetailCourseInSpecialTag(loadVersion, tag);
        if (CollUtil.isNotEmpty(gkCourses)) {
            List<Long> courseDataIds = new ArrayList<>();
            List<GkLesson> lessons = new ArrayList<>();
            List<GkSection> sections = new ArrayList<>();
            Map<Integer, String> courseDetailMap = new HashMap<>();
            gkCourses.forEach(gkCourse -> {
                courseDataIds.add(gkCourse.getId());
                log.debug("正在加载课程详情：{},年级：{},", gkCourse.getName(), gkCourse.getGradeId());
                Map<String, Object> courseDetailParams = new HashMap<>();
                courseDetailParams.put("course_id", gkCourse.getCourseId());
                courseDetailParams.put("ignore_status", 0);
                String result = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_GET_COURSE_DETAIL_API, courseDetailParams);
                if (result != null && JSONUtil.isTypeJSON(result)) {
                    JSONObject jsonObject = new JSONObject(result);
                    if (jsonObject.getInt("code") == 10000) {
                        String description = jsonObject.getJSONObject("data").getStr("description");
                        courseDetailMap.put(gkCourse.getCourseId(), description);
                        JSONArray lessonsArray = jsonObject.getJSONObject("data").getJSONArray("lessons");
                        if (lessonsArray != null) {
                            lessonsArray.forEach(lessonObj -> {
                                JSONObject lessonObject = (JSONObject) lessonObj;
                                GkLesson lesson = new GkLesson();
                                Integer lessonId = lessonObject.getInt("id");
                                lesson.setLessonId(lessonId);
                                lesson.setCourseId(gkCourse.getCourseId());
                                lesson.setName(lessonObject.getStr("name"));
                                lesson.setLoadVersion(gkCourse.getLoadVersion());
                                lessons.add(lesson);
                                JSONArray sectionArray = lessonObject.getJSONArray("section");
                                if (sectionArray != null) {
                                    for (int i = 0; i < sectionArray.size(); i++) {
                                        JSONObject sectionObject = (JSONObject) sectionArray.get(i);
                                        GkSection section = new GkSection();
                                        Integer sectionId = sectionObject.getInt("id");
                                        section.setSectionId(sectionId);
                                        section.setLessonId(lessonId);
                                        section.setName(sectionObject.getStr("name"));
                                        section.setCcvid(sectionObject.getStr("ccvid"));
                                        section.setHasQuestion(sectionObject.getInt("has_question"));
                                        section.setInProduction(sectionObject.getBool("inProduction"));
                                        section.setVideoId(sectionObject.getStr("video_id"));
                                        section.setCoverImg(sectionObject.getStr("cover_img"));
                                        section.setUnitTestAddress(sectionObject.getStr("unitTestAddress"));
                                        section.setDuration(sectionObject.getInt("duration"));
                                        section.setFree(sectionObject.getInt("free"));
                                        section.setType(sectionObject.getInt("type"));
                                        section.setCourseId(gkCourse.getCourseId());
                                        section.setLoadVersion(gkCourse.getLoadVersion());
                                        sections.add(section);
                                    }
                                }
                            });
                        }
                    } else {
                        System.out.println("课程=" + gkCourse.getName() + "，错误=" + result);
                    }
                }
            });
            ganKaoRepository.saveBatchLesson(courseDataIds, lessons, sections);
            ganKaoRepository.updateCourseLoadStatusAndDetail(loadVersion, courseDetailMap);
            ganKaoRepository.finishCourseDetailLoadTaskByLoadVersion(loadVersion);
        }

    }

    public void requestCourseDetailByCourseIds(Long loadVersion, List<Integer> courseIds) {
        List<Long> courseDataIds = new ArrayList<>();
        List<GkLesson> lessons = new ArrayList<>();
        List<GkSection> sections = new ArrayList<>();
        Map<Integer, String> courseDetailMap = new HashMap<>();
        List<GkCourse> gkCourses = ganKaoRepository.selectCourseByCourseIds(loadVersion, courseIds);
        gkCourses.forEach(gkCourse -> {
            courseDataIds.add(gkCourse.getId());
            log.debug("正在加载课程详情：{},年级：{},", gkCourse.getName(), gkCourse.getGradeId());
            Map<String, Object> courseDetailParams = new HashMap<>();
            courseDetailParams.put("course_id", gkCourse.getCourseId());
            courseDetailParams.put("ignore_status", 0);
            String result = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_GET_COURSE_DETAIL_API, courseDetailParams);
            if (result != null && JSONUtil.isTypeJSON(result)) {
                JSONObject jsonObject = new JSONObject(result);
                if (jsonObject.getInt("code") == 10000) {
                    String description = jsonObject.getJSONObject("data").getStr("description");
                    courseDetailMap.put(gkCourse.getCourseId(), description);
                    JSONArray lessonsArray = jsonObject.getJSONObject("data").getJSONArray("lessons");
                    if (lessonsArray != null) {
                        lessonsArray.forEach(lessonObj -> {
                            JSONObject lessonObject = (JSONObject) lessonObj;
                            GkLesson lesson = new GkLesson();
                            Integer lessonId = lessonObject.getInt("id");
                            lesson.setLessonId(lessonId);
                            lesson.setCourseId(gkCourse.getCourseId());
                            lesson.setName(lessonObject.getStr("name"));
                            lesson.setLoadVersion(gkCourse.getLoadVersion());
                            lessons.add(lesson);
                            JSONArray sectionArray = lessonObject.getJSONArray("section");
                            if (sectionArray != null) {
                                for (int i = 0; i < sectionArray.size(); i++) {
                                    JSONObject sectionObject = (JSONObject) sectionArray.get(i);
                                    GkSection section = new GkSection();
                                    Integer sectionId = sectionObject.getInt("id");
                                    section.setSectionId(sectionId);
                                    section.setLessonId(lessonId);
                                    section.setName(sectionObject.getStr("name"));
                                    section.setCcvid(sectionObject.getStr("ccvid"));
                                    section.setHasQuestion(sectionObject.getInt("has_question"));
                                    section.setInProduction(sectionObject.getBool("inProduction"));
                                    section.setVideoId(sectionObject.getStr("video_id"));
                                    section.setCoverImg(sectionObject.getStr("cover_img"));
                                    section.setUnitTestAddress(sectionObject.getStr("unitTestAddress"));
                                    section.setDuration(sectionObject.getInt("duration"));
                                    section.setFree(sectionObject.getInt("free"));
                                    section.setType(sectionObject.getInt("type"));
                                    section.setCourseId(gkCourse.getCourseId());
                                    section.setLoadVersion(gkCourse.getLoadVersion());
                                    sections.add(section);
                                }
                            }
                        });
                    }
                } else {
                    System.out.println("课程id=" + gkCourse.getCourseId() + "课程=" + gkCourse.getName() + "，错误=" + result);
                }
            }
        });
        ganKaoRepository.saveBatchLesson(courseDataIds, lessons, sections);
        ganKaoRepository.updateCourseLoadStatusAndDetail(loadVersion, courseDetailMap);
        ganKaoRepository.finishCourseDetailLoadTaskByLoadVersion(loadVersion);
    }

    public void requestSpecialCourseByLoadVersion(Long loadVersion) throws Exception {
        // 使用分布式锁确保同一操作的幂等性
        String lockKey = "GANKAO_LOAD_SPEC_LOCK:" + loadVersion;
        ZLock zLock = null;
        try {
            // 尝试获取锁，等待最多3秒，锁定30秒
            zLock = distributedLock.tryLock(lockKey, 3, TimeUnit.SECONDS);
            CheckUtils.throwIf(zLock == null, "获取锁失败，请稍后重试");
            for (GanKaoSpecialTags tagEnum : GanKaoSpecialTags.values()) {
                String tag = tagEnum.name();
                Map<String, Object> specialTagParams = new HashMap<>();
                specialTagParams.put("tagcode", tag);

                System.out.println("正在加载特殊课程：" + tag);
                String result = ganKaoHttpUtil.requestApi(GanKaoHttpParams.URI_GET_SPECIAL_TAG_COURSES_API, specialTagParams);
                Thread.sleep(3000);
                if (result != null && JSONUtil.isTypeJSON(result)) {
                    JSONObject jsonObject = new JSONObject(result);
                    if (jsonObject.getInt("code") == 10000) {
                        JSONArray coursesWeightArray = jsonObject.getJSONArray("data");
                        if (coursesWeightArray != null) {
                            System.out.println("正在加载特殊课程：" + tag + ",数量：" + coursesWeightArray.size());
                            List<GkSpecialTagCourse> gkSpecialTagCourses = new ArrayList<>();
                            List<GkCourse> gkCourses = new ArrayList<>();
                            List<GkSubjectCategoryCourseTypeTag> gkSubjectCategoryCourseTypeTagList = new ArrayList<>();
                            coursesWeightArray.forEach(coursesWeightJson -> {
                                JSONObject coursesWeightJsonObj = (JSONObject) coursesWeightJson;
                                Integer weight = coursesWeightJsonObj.getInt("weight");
                                JSONObject courseObject = coursesWeightJsonObj.getJSONObject("course");
                                Integer courseId = courseObject.getInt("id");
                                GkSpecialTagCourse gkSpecialTagCourse = new GkSpecialTagCourse();
                                gkSpecialTagCourse.setCourseId(courseId);
                                gkSpecialTagCourse.setLoadVersion(loadVersion);
                                gkSpecialTagCourse.setSpecTag(tag);
                                gkSpecialTagCourse.setWeight(weight);
                                gkSpecialTagCourses.add(gkSpecialTagCourse);
                                GkCourse gkCourse = new GkCourse();
                                gkCourse.setCourseId(courseId);
                                gkCourse.setName(courseObject.getStr("name"));
                                gkCourse.setCateId(0);
                                gkCourse.setPrice(courseObject.getBigDecimal("marked_price"));
                                gkCourse.setSubjectId(0);
                                gkCourse.setGradeId(0);
                                gkCourse.setMediaType(1);
                                gkCourse.setTitlePic(courseObject.getStr("title_pic"));
                                gkCourse.setViews(courseObject.getStr("views"));
                                //这个接口不一样返回的是
                                // {"id":215,"teacher_name":"田宏杰（心理辅导）","title_pic":"c/tian/bt.jpg",
                                // "user_id":80,"title_pic_show":"https://img.qiaoxuesi.com/c/tian/bt.jpg"}
                                //JSONObject t1=courseObject.getJSONObject("teacher");
                                gkCourse.setTeacherName("");
                                gkCourse.setPic(courseObject.getStr("title_pic_show"));
                                gkCourse.setLoadStatus(0);
                                gkCourse.setLoadVersion(loadVersion);
                                GkSubjectCategoryCourseTypeTag gkSubjectCategoryCourseTypeTag = new GkSubjectCategoryCourseTypeTag();
                                gkSubjectCategoryCourseTypeTag.setLoadVersion(loadVersion);
                                gkSubjectCategoryCourseTypeTag.setCourseId(courseId);
                                gkSubjectCategoryCourseTypeTag.setSubjectId(0);
                                gkSubjectCategoryCourseTypeTag.setCateId(0);
                                gkSubjectCategoryCourseTypeTag.setParentId(0);
                                gkSubjectCategoryCourseTypeTag.setGradeId(0);
                                gkSubjectCategoryCourseTypeTag.setTag(tag);
                                gkSubjectCategoryCourseTypeTag.setCourseType(0);
                                gkCourses.add(gkCourse);
                                gkSubjectCategoryCourseTypeTagList.add(gkSubjectCategoryCourseTypeTag);

                            });
                            ganKaoRepository.saveBatchSpecTagCourse(gkSpecialTagCourses);
                            ganKaoRepository.saveBatchCourse(gkCourses);
                            ganKaoRepository.saveBatchCourseCategoryCourseTypeTag(gkSubjectCategoryCourseTypeTagList);
                            ganKaoRepository.finishSpecialLoadTaskByLoadVersion(loadVersion);
                        }
                    } else {
                        System.out.println("正在加载特殊课程：" + tag + ",result：" + result);
                    }

                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (zLock != null) {
                zLock.close();
            }
        }
    }

    public void testEnv() {
        ganKaoHttpUtil.testUrl();
    }
}